import sys
import os
import cv2
import numpy as np
from PyQt5.QtGui import (QPixmap, QImage, QPainter, QPen, QColor, QIcon, QFont,
                         QTransform, QPalette, QBrush)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QPoint, QSize
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QPushButton, QFileDialog, QSlider, QSpinBox, QComboBox,
                             QGroupBox, QGridLayout, QTabWidget, QSplitter, QMessageBox,
                             QAction, QMenuBar, QToolBar, QStatusBar, QScrollArea, QFrame, QTextEdit, QLineEdit, QListWidget)

# 确保中文显示正常
font = QtGui.QFont()
font.setFamily("SimHei")


class ImageProcessor:
    """图像处理核心类，负责各种图像处理算法"""

    def __init__(self):
        self.original_image = None
        self.processed_image = None
        self.current_image = None
        self.faces_db = {}  # 人脸数据库

    def load_image(self, file_path):
        """加载图像"""
        try:
            self.original_image = cv2.imread(file_path)
            if self.original_image is None:
                return False
            self.processed_image = self.original_image.copy()
            self.current_image = self.original_image.copy()
            return True
        except Exception as e:
            print(f"加载图像时出错: {e}")
            return False

    def rotate_image(self, angle):
        """旋转图像"""
        if self.processed_image is None:
            return None
        try:
            (h, w) = self.processed_image.shape[:2]
            center = (w // 2, h // 2)
            M = cv2.getRotationMatrix2D(center, angle, 1.0)
            self.processed_image = cv2.warpAffine(self.processed_image, M, (w, h))
            self.current_image = self.processed_image.copy()
            return self.processed_image
        except Exception as e:
            print(f"旋转图像时出错: {e}")
            return None

    def scale_image(self, scale_percent):
        """缩放图像"""
        if self.processed_image is None:
            return None
        try:
            scale = scale_percent / 100.0
            width = int(self.processed_image.shape[1] * scale)
            height = int(self.processed_image.shape[0] * scale)
            dim = (width, height)
            self.processed_image = cv2.resize(self.processed_image, dim, interpolation=cv2.INTER_AREA)
            self.current_image = self.processed_image.copy()
            return self.processed_image
        except Exception as e:
            print(f"缩放图像时出错: {e}")
            return None

    def crop_image(self, x, y, width, height):
        """裁剪图像"""
        if self.processed_image is None:
            return None
        try:
            self.processed_image = self.processed_image[y:y + height, x:x + width]
            self.current_image = self.processed_image.copy()
            return self.processed_image
        except Exception as e:
            print(f"裁剪图像时出错: {e}")
            return None

    def adjust_brightness_contrast(self, brightness=0, contrast=0):
        """调整亮度和对比度"""
        if self.processed_image is None:
            return None
        try:
            if brightness != 0:
                if brightness > 0:
                    shadow = brightness
                    highlight = 255
                else:
                    shadow = 0
                    highlight = 255 + brightness
                alpha_b = (highlight - shadow) / 255
                gamma_b = shadow

                buf = cv2.addWeighted(self.processed_image, alpha_b,
                                      self.processed_image, 0, gamma_b)
            else:
                buf = self.processed_image.copy()

            if contrast != 0:
                f = 131 * (contrast + 127) / (127 * (131 - contrast))
                alpha_c = f
                gamma_c = 127 * (1 - f)

                buf = cv2.addWeighted(buf, alpha_c,
                                      buf, 0, gamma_c)

            self.processed_image = buf
            self.current_image = self.processed_image.copy()
            return self.processed_image
        except Exception as e:
            print(f"调整亮度和对比度时出错: {e}")
            return None

    def smooth_image(self, method='Gaussian', kernel_size=3):
        """图像平滑处理"""
        if self.processed_image is None:
            return None
        try:
            if method == 'Gaussian':
                self.processed_image = cv2.GaussianBlur(self.processed_image, (kernel_size, kernel_size), 0)
            elif method == 'Median':
                self.processed_image = cv2.medianBlur(self.processed_image, kernel_size)
            elif method == 'Bilateral':
                self.processed_image = cv2.bilateralFilter(self.processed_image, kernel_size, 75, 75)

            self.current_image = self.processed_image.copy()
            return self.processed_image
        except Exception as e:
            print(f"图像平滑处理时出错: {e}")
            return None

    def detect_shapes(self):
        """形状识别"""
        if self.processed_image is None:
            return None, []
        try:
            # 复制当前图像用于处理
            img = self.processed_image.copy()
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

            # 寻找轮廓
            contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

            shapes = []
            for cnt in contours:
                # 计算轮廓面积，过滤小面积
                area = cv2.contourArea(cnt)
                if area < 100:
                    continue

                # 计算轮廓周长
                perimeter = cv2.arcLength(cnt, True)
                # 多边形逼近
                approx = cv2.approxPolyDP(cnt, 0.04 * perimeter, True)

                # 根据顶点数量判断形状
                if len(approx) == 3:
                    shape = "三角形"
                elif len(approx) == 4:
                    shape = "矩形"
                elif 5 <= len(approx) <= 10:
                    shape = "多边形"
                else:
                    # 计算轮廓与圆形的相似度
                    (x, y), radius = cv2.minEnclosingCircle(cnt)
                    circle_area = np.pi * radius ** 2
                    similarity = area / circle_area
                    if similarity > 0.8:
                        shape = "圆形"
                    else:
                        shape = "未知"

                # 记录形状信息
                shapes.append({
                    'type': shape,
                    'contour': cnt,
                    'center': (int(x), int(y)),
                    'area': area
                })

                # 在图像上绘制轮廓和标记
                cv2.drawContours(img, [cnt], -1, (0, 255, 0), 2)
                cv2.putText(img, shape, (int(x) - 20, int(y)),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)

            self.current_image = img
            return self.current_image, shapes
        except Exception as e:
            print(f"形状识别时出错: {e}")
            return None, []

    def detect_faces(self):
        """人脸检测"""
        if self.processed_image is None:
            return None, []
        try:
            # 复制当前图像用于处理
            img = self.processed_image.copy()
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

            # 加载人脸检测器
            face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

            # 检测人脸
            faces = face_cascade.detectMultiScale(gray, 1.3, 5)

            faces_info = []
            for (x, y, w, h) in faces:
                # 在图像上绘制人脸矩形
                cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)

                # 提取人脸区域
                face_roi = gray[y:y + h, x:x + w]

                # 记录人脸信息
                faces_info.append({
                    'rect': (x, y, w, h),
                    'face_roi': face_roi
                })

            self.current_image = img
            return self.current_image, faces_info
        except Exception as e:
            print(f"人脸检测时出错: {e}")
            return None, []

    def recognize_faces(self):
        """人脸识别"""
        if self.processed_image is None:
            return None, []
        if not self.faces_db:
            QMessageBox.warning(None, "警告", "人脸数据库为空，请先注册人脸")
            return self.processed_image, []
        try:
            # 复制当前图像用于处理
            img = self.processed_image.copy()
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

            # 加载人脸检测器
            face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

            # 检测人脸
            faces = face_cascade.detectMultiScale(gray, 1.3, 5)

            faces_info = []
            for (x, y, w, h) in faces:
                # 提取人脸区域
                face_roi = gray[y:y + h, x:x + w]

                # 寻找最匹配的人脸
                best_match = None
                min_distance = float('inf')

                for name, db_face in self.faces_db.items():
                    # 计算人脸之间的差异
                    if face_roi.shape == db_face.shape:
                        diff = cv2.absdiff(face_roi, db_face)
                        distance = np.sum(diff)

                        if distance < min_distance:
                            min_distance = distance
                            best_match = name

                # 在图像上绘制人脸矩形和识别结果
                cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
                if best_match:
                    similarity = 100 - min(100, min_distance / 1000)  # 简单相似度计算
                    text = f"{best_match} ({similarity:.1f}%)"
                    cv2.putText(img, text, (x, y - 10),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 0), 2)

                    # 记录人脸信息
                    faces_info.append({
                        'name': best_match,
                        'similarity': similarity,
                        'rect': (x, y, w, h)
                    })

            self.current_image = img
            return self.current_image, faces_info
        except Exception as e:
            print(f"人脸识别时出错: {e}")
            return None, []

    def register_face(self, name):
        """注册人脸到数据库"""
        if self.processed_image is None:
            QMessageBox.warning(None, "警告", "请先加载包含人脸的图像")
            return False
        try:
            # 复制当前图像用于处理
            gray = cv2.cvtColor(self.processed_image, cv2.COLOR_BGR2GRAY)

            # 加载人脸检测器
            face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

            # 检测人脸
            faces = face_cascade.detectMultiScale(gray, 1.3, 5)

            if len(faces) == 0:
                QMessageBox.warning(None, "警告", "未检测到人脸")
                return False

            if len(faces) > 1:
                QMessageBox.warning(None, "警告", "检测到多个人脸，请确保图像中只有一个人脸")
                return False

            # 提取人脸区域
            x, y, w, h = faces[0]
            face_roi = gray[y:y + h, x:x + w]

            # 注册人脸到数据库
            self.faces_db[name] = face_roi
            QMessageBox.information(None, "成功", f"已成功注册人脸: {name}")
            self.update_face_list()
            return True
        except Exception as e:
            print(f"注册人脸时出错: {e}")
            return False

    def stitch_images(self, images):
        """图像拼接"""
        if len(images) < 2:
            QMessageBox.warning(None, "警告", "至少需要两张图像进行拼接")
            return None
        try:
            stitcher = cv2.Stitcher_create()
            status, result = stitcher.stitch(images)

            if status == cv2.Stitcher_OK:
                self.processed_image = result
                self.current_image = result
                return result
            else:
                QMessageBox.warning(None, "错误", f"图像拼接失败，错误代码: {status}")
                return None
        except Exception as e:
            print(f"图像拼接时出错: {e}")
            return None

    def sharpen_image(self):
        """图像锐化"""
        if self.processed_image is None:
            return None
        try:
            kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
            self.processed_image = cv2.filter2D(self.processed_image, -1, kernel)
            self.current_image = self.processed_image.copy()
            return self.processed_image
        except Exception as e:
            print(f"图像锐化时出错: {e}")
            return None

    def edge_detection(self):
        """边缘检测"""
        if self.processed_image is None:
            return None
        try:
            gray = cv2.cvtColor(self.processed_image, cv2.COLOR_BGR2GRAY)
            edges = cv2.Canny(gray, 50, 150)
            self.processed_image = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
            self.current_image = self.processed_image.copy()
            return self.processed_image
        except Exception as e:
            print(f"边缘检测时出错: {e}")
            return None


class VideoThread(QThread):
    """视频处理线程，用于实时处理视频流"""
    change_pixmap_signal = pyqtSignal(np.ndarray)

    def __init__(self, processor, detection_type='none'):
        super().__init__()
        self.processor = processor
        self.detection_type = detection_type
        self._run_flag = True

    def run(self):
        # 打开摄像头或视频文件
        if self.detection_type == 'file':
            file_path, _ = QFileDialog.getOpenFileName(
                None, "选择视频文件", "", "视频文件 (*.mp4 *.avi *.mov *.mkv)"
            )
            if not file_path:
                return
            cap = cv2.VideoCapture(file_path)
        else:
            cap = cv2.VideoCapture(0)

        if not cap.isOpened():
            QMessageBox.warning(None, "错误", "无法打开视频文件或摄像头")
            return

        while self._run_flag:
            ret, frame = cap.read()
            if ret:
                # 镜像翻转，使显示更直观
                frame = cv2.flip(frame, 1)

                # 根据选择的检测类型处理帧
                if self.detection_type == 'faces':
                    self.processor.processed_image = frame
                    frame, _ = self.processor.detect_faces()
                elif self.detection_type == 'shapes':
                    self.processor.processed_image = frame
                    frame, _ = self.processor.detect_shapes()

                # 发送信号更新显示
                self.change_pixmap_signal.emit(frame)
            else:
                break

        # 释放摄像头或视频文件
        cap.release()

    def stop(self):
        """停止线程"""
        self._run_flag = False
        self.wait()


class ImageCanvas(QLabel):
    """自定义图像显示区域，支持缩放、拖动等操作"""

    crop_selected = pyqtSignal(int, int, int, int)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAlignment(Qt.AlignCenter)
        self.setMinimumSize(1, 1)  # 允许QLabel收缩到最小
        self.pixmap = None
        self.scale_factor = 1.0
        self.dragging = False
        self.last_pos = QPoint()
        self.crop_start = None
        self.crop_end = None
        self.cropping = False
        self.show_grid = False

    def setPixmap(self, pixmap):
        """设置图像"""
        self.pixmap = pixmap
        super().setPixmap(self.scaledPixmap())

    def scaledPixmap(self):
        """返回缩放后的图像"""
        if self.pixmap and not self.pixmap.isNull():
            return self.pixmap.scaled(
                self.pixmap.width() * self.scale_factor,
                self.pixmap.height() * self.scale_factor,
                Qt.KeepAspectRatio,
                Qt.SmoothTransformation
            )
        return QPixmap()

    def wheelEvent(self, event):
        """鼠标滚轮事件 - 用于缩放"""
        if self.pixmap and not self.pixmap.isNull():
            delta = event.angleDelta().y()
            if delta > 0:
                self.scale_factor *= 1.1
            else:
                self.scale_factor *= 0.9

            self.scale_factor = max(0.1, min(self.scale_factor, 10.0))
            super().setPixmap(self.scaledPixmap())

    def mousePressEvent(self, event):
        """鼠标按下事件"""
        if event.button() == Qt.LeftButton:
            self.dragging = True
            self.last_pos = event.pos()

            # 如果是裁剪模式
            if self.cropping:
                self.crop_start = event.pos()
                self.crop_end = None

    def mouseMoveEvent(self, event):
        """鼠标移动事件"""
        if self.dragging:
            # 计算位移
            delta = event.pos() - self.last_pos
            self.last_pos = event.pos()

            # 移动滚动条
            scroll_area = self.parent()
            if scroll_area:
                h_bar = scroll_area.horizontalScrollBar()
                v_bar = scroll_area.verticalScrollBar()
                h_bar.setValue(h_bar.value() - delta.x())
                v_bar.setValue(v_bar.value() - delta.y())

        # 如果是裁剪模式
        if self.cropping and self.crop_start:
            self.crop_end = event.pos()
            self.update()  # 触发paintEvent以绘制裁剪框

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        if event.button() == Qt.LeftButton:
            self.dragging = False

            # 如果是裁剪模式
            if self.cropping and self.crop_start and self.crop_end:
                # 确保起点和终点正确
                x1 = min(self.crop_start.x(), self.crop_end.x())
                y1 = min(self.crop_start.y(), self.crop_end.y())
                x2 = max(self.crop_start.x(), self.crop_end.x())
                y2 = max(self.crop_start.y(), self.crop_end.y())

                # 计算相对于原始图像的坐标
                original_width = self.pixmap.width()
                original_height = self.pixmap.height()
                scaled_width = original_width * self.scale_factor
                scaled_height = original_height * self.scale_factor

                # 计算在视口中的偏移
                scroll_area = self.parent()
                if scroll_area:
                    h_offset = scroll_area.horizontalScrollBar().value()
                    v_offset = scroll_area.verticalScrollBar().value()

                    # 计算裁剪区域相对于原始图像的坐标
                    x = int((x1 + h_offset) / self.scale_factor)
                    y = int((y1 + v_offset) / self.scale_factor)
                    width = int((x2 - x1) / self.scale_factor)
                    height = int((y2 - y1) / self.scale_factor)

                    # 确保坐标在有效范围内
                    x = max(0, min(x, original_width - 1))
                    y = max(0, min(y, original_height - 1))
                    width = max(1, min(width, original_width - x))
                    height = max(1, min(height, original_height - y))

                    # 发出裁剪信号
                    self.crop_selected.emit(x, y, width, height)

    def paintEvent(self, event):
        """绘制事件"""
        super().paintEvent(event)

        # 如果有图像且正在裁剪，绘制裁剪框
        if self.pixmap and not self.pixmap.isNull() and self.cropping and self.crop_start and self.crop_end:
            painter = QPainter(self)
            painter.setPen(QPen(QColor(255, 0, 0), 2, Qt.DashLine))

            # 绘制裁剪矩形
            x = min(self.crop_start.x(), self.crop_end.x())
            y = min(self.crop_start.y(), self.crop_end.y())
            w = abs(self.crop_start.x() - self.crop_end.x())
            h = abs(self.crop_start.y() - self.crop_end.y())
            painter.drawRect(x, y, w, h)

            # 如果启用网格，绘制网格
            if self.show_grid:
                painter.setPen(QPen(QColor(100, 100, 100), 1, Qt.DotLine))

                # 计算网格大小
                grid_size = 20

                # 绘制垂直线
                for i in range(x, x + w, grid_size):
                    painter.drawLine(i, y, i, y + h)

                # 绘制水平线
                for i in range(y, y + h, grid_size):
                    painter.drawLine(x, i, x + w, i)

    def enterEvent(self, event):
        """鼠标进入事件"""
        self.setCursor(Qt.OpenHandCursor)

    def leaveEvent(self, event):
        """鼠标离开事件"""
        self.setCursor(Qt.ArrowCursor)

    def setCropping(self, cropping):
        """设置是否处于裁剪模式"""
        self.cropping = cropping
        self.crop_start = None
        self.crop_end = None
        self.update()

    def resetView(self):
        """重置视图"""
        self.scale_factor = 1.0
        if self.pixmap:
            super().setPixmap(self.scaledPixmap())


class MainWindow(QMainWindow):
    """主窗口类"""

    def __init__(self):
        super().__init__()

        # 设置中文字体
        self.setFont(font)

        # 初始化图像处理引擎
        self.processor = ImageProcessor()

        # 设置窗口标题和大小
        self.setWindowTitle("图像处理与计算机视觉平台")
        self.resize(1200, 800)

        # 创建中央部件和布局
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.main_layout = QHBoxLayout(self.central_widget)

        # 创建左侧工具栏
        self.create_toolbar()

        # 创建中央显示区域
        self.create_display_area()

        # 创建右侧参数面板
        self.create_settings_panel()

        # 创建状态栏
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        self.statusBar.showMessage("就绪")

        # 初始化视频处理线程
        self.video_thread = None

        # 连接信号和槽
        self.connect_signals()

        # 初始化裁剪模式
        self.crop_mode = False

    def create_toolbar(self):
        """创建左侧工具栏"""
        self.toolbar_widget = QWidget()
        self.toolbar_layout = QVBoxLayout(self.toolbar_widget)
        self.toolbar_widget.setMaximumWidth(120)

        # 文件操作组
        file_group = QGroupBox("文件操作")
        file_layout = QVBoxLayout(file_group)

        self.load_image_btn = QPushButton("加载图像")
        self.load_video_btn = QPushButton("加载视频")
        self.open_camera_btn = QPushButton("打开摄像头")
        self.save_btn = QPushButton("保存结果")

        file_layout.addWidget(self.load_image_btn)
        file_layout.addWidget(self.load_video_btn)
        file_layout.addWidget(self.open_camera_btn)
        file_layout.addWidget(self.save_btn)

        # 图像处理组
        image_processing_group = QGroupBox("图像处理")
        image_processing_layout = QVBoxLayout(image_processing_group)

        self.rotate_btn = QPushButton("旋转")
        self.scale_btn = QPushButton("缩放")
        self.crop_btn = QPushButton("裁剪")
        self.color_adjust_btn = QPushButton("色彩调整")
        self.smooth_btn = QPushButton("平滑处理")
        self.sharpen_btn = QPushButton("锐化处理")
        self.edge_detection_btn = QPushButton("边缘检测")

        image_processing_layout.addWidget(self.rotate_btn)
        image_processing_layout.addWidget(self.scale_btn)
        image_processing_layout.addWidget(self.crop_btn)
        image_processing_layout.addWidget(self.color_adjust_btn)
        image_processing_layout.addWidget(self.smooth_btn)
        image_processing_layout.addWidget(self.sharpen_btn)
        image_processing_layout.addWidget(self.edge_detection_btn)

        # 计算机视觉组
        cv_group = QGroupBox("计算机视觉")
        cv_layout = QVBoxLayout(cv_group)

        self.shape_detection_btn = QPushButton("形状识别")
        self.face_detection_btn = QPushButton("人脸检测")
        self.face_recognition_btn = QPushButton("人脸识别")
        self.register_face_btn = QPushButton("注册人脸")
        self.stitch_images_btn = QPushButton("图像拼接")

        cv_layout.addWidget(self.shape_detection_btn)
        cv_layout.addWidget(self.face_detection_btn)
        cv_layout.addWidget(self.face_recognition_btn)
        cv_layout.addWidget(self.register_face_btn)
        cv_layout.addWidget(self.stitch_images_btn)

        # 添加所有组到工具栏布局
        self.toolbar_layout.addWidget(file_group)
        self.toolbar_layout.addWidget(image_processing_group)
        self.toolbar_layout.addWidget(cv_group)
        self.toolbar_layout.addStretch()

        # 将工具栏添加到主布局
        self.main_layout.addWidget(self.toolbar_widget)

    def create_display_area(self):
        """创建中央显示区域"""
        self.display_widget = QWidget()
        self.display_layout = QVBoxLayout(self.display_widget)

        # 创建图像显示区域
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        self.image_canvas = ImageCanvas(self.scroll_area)
        self.scroll_area.setWidget(self.image_canvas)
        self.image_canvas.crop_selected.connect(self.on_crop_selected)

        # 创建结果显示区域
        self.results_area = QTextEdit()
        self.results_area.setReadOnly(True)
        self.results_area.setMinimumHeight(150)

        # 添加到布局
        self.display_layout.addWidget(self.scroll_area, 8)
        self.display_layout.addWidget(self.results_area, 2)

        # 将显示区域添加到主布局
        self.main_layout.addWidget(self.display_widget, 3)

    def create_settings_panel(self):
        """创建右侧参数面板"""
        self.settings_widget = QWidget()
        self.settings_layout = QVBoxLayout(self.settings_widget)
        self.settings_widget.setMaximumWidth(300)

        # 创建选项卡控件
        self.settings_tabs = QTabWidget()

        # 旋转设置选项卡
        self.rotate_tab = QWidget()
        self.rotate_layout = QVBoxLayout(self.rotate_tab)

        self.rotate_label = QLabel("旋转角度:")
        self.rotate_slider = QSlider(Qt.Horizontal)
        self.rotate_slider.setRange(0, 359)
        self.rotate_slider.setValue(0)
        self.rotate_value = QLabel("0°")

        self.rotate_apply_btn = QPushButton("应用旋转")

        rotate_slider_layout = QHBoxLayout()
        rotate_slider_layout.addWidget(self.rotate_slider)
        rotate_slider_layout.addWidget(self.rotate_value)

        self.rotate_layout.addWidget(self.rotate_label)
        self.rotate_layout.addLayout(rotate_slider_layout)
        self.rotate_layout.addWidget(self.rotate_apply_btn)
        self.rotate_layout.addStretch()

        # 缩放设置选项卡
        self.scale_tab = QWidget()
        self.scale_layout = QVBoxLayout(self.scale_tab)

        self.scale_label = QLabel("缩放比例:")
        self.scale_slider = QSlider(Qt.Horizontal)
        self.scale_slider.setRange(10, 500)
        self.scale_slider.setValue(100)
        self.scale_value = QLabel("100%")

        self.scale_apply_btn = QPushButton("应用缩放")

        scale_slider_layout = QHBoxLayout()
        scale_slider_layout.addWidget(self.scale_slider)
        scale_slider_layout.addWidget(self.scale_value)

        self.scale_layout.addWidget(self.scale_label)
        self.scale_layout.addLayout(scale_slider_layout)
        self.scale_layout.addWidget(self.scale_apply_btn)
        self.scale_layout.addStretch()

        # 裁剪设置选项卡
        self.crop_tab = QWidget()
        self.crop_layout = QVBoxLayout(self.crop_tab)

        self.crop_x_label = QLabel("左上角 X:")
        self.crop_x_spin = QSpinBox()
        self.crop_x_spin.setRange(0, 10000)

        self.crop_y_label = QLabel("左上角 Y:")
        self.crop_y_spin = QSpinBox()
        self.crop_y_spin.setRange(0, 10000)

        self.crop_width_label = QLabel("宽度:")
        self.crop_width_spin = QSpinBox()
        self.crop_width_spin.setRange(1, 10000)

        self.crop_height_label = QLabel("高度:")
        self.crop_height_spin = QSpinBox()
        self.crop_height_spin.setRange(1, 10000)

        self.crop_apply_btn = QPushButton("应用裁剪")
        self.crop_auto_btn = QPushButton("自动检测")

        crop_buttons_layout = QHBoxLayout()
        crop_buttons_layout.addWidget(self.crop_auto_btn)
        crop_buttons_layout.addWidget(self.crop_apply_btn)

        self.crop_layout.addWidget(self.crop_x_label)
        self.crop_layout.addWidget(self.crop_x_spin)
        self.crop_layout.addWidget(self.crop_y_label)
        self.crop_layout.addWidget(self.crop_y_spin)
        self.crop_layout.addWidget(self.crop_width_label)
        self.crop_layout.addWidget(self.crop_width_spin)
        self.crop_layout.addWidget(self.crop_height_label)
        self.crop_layout.addWidget(self.crop_height_spin)
        self.crop_layout.addLayout(crop_buttons_layout)
        self.crop_layout.addStretch()

        # 色彩调整选项卡
        self.color_adjust_tab = QWidget()
        self.color_adjust_layout = QVBoxLayout(self.color_adjust_tab)

        # 亮度调整
        self.brightness_label = QLabel("亮度:")
        self.brightness_slider = QSlider(Qt.Horizontal)
        self.brightness_slider.setRange(-100, 100)
        self.brightness_slider.setValue(0)
        self.brightness_value = QLabel("0")

        brightness_slider_layout = QHBoxLayout()
        brightness_slider_layout.addWidget(self.brightness_slider)
        brightness_slider_layout.addWidget(self.brightness_value)

        # 对比度调整
        self.contrast_label = QLabel("对比度:")
        self.contrast_slider = QSlider(Qt.Horizontal)
        self.contrast_slider.setRange(-100, 100)
        self.contrast_slider.setValue(0)
        self.contrast_value = QLabel("0")

        contrast_slider_layout = QHBoxLayout()
        contrast_slider_layout.addWidget(self.contrast_slider)
        contrast_slider_layout.addWidget(self.contrast_value)

        self.color_apply_btn = QPushButton("应用调整")

        self.color_adjust_layout.addWidget(self.brightness_label)
        self.color_adjust_layout.addLayout(brightness_slider_layout)
        self.color_adjust_layout.addWidget(self.contrast_label)
        self.color_adjust_layout.addLayout(contrast_slider_layout)
        self.color_adjust_layout.addWidget(self.color_apply_btn)
        self.color_adjust_layout.addStretch()

        # 平滑处理选项卡
        self.smooth_tab = QWidget()
        self.smooth_layout = QVBoxLayout(self.smooth_tab)

        self.smooth_method_label = QLabel("平滑方法:")
        self.smooth_method_combo = QComboBox()
        self.smooth_method_combo.addItems(["高斯模糊", "中值滤波", "双边滤波"])

        self.smooth_kernel_label = QLabel("核大小:")
        self.smooth_kernel_spin = QSpinBox()
        self.smooth_kernel_spin.setRange(1, 21)
        self.smooth_kernel_spin.setValue(3)
        self.smooth_kernel_spin.setSingleStep(2)  # 确保是奇数

        self.smooth_apply_btn = QPushButton("应用平滑")

        self.smooth_layout.addWidget(self.smooth_method_label)
        self.smooth_layout.addWidget(self.smooth_method_combo)
        self.smooth_layout.addWidget(self.smooth_kernel_label)
        self.smooth_layout.addWidget(self.smooth_kernel_spin)
        self.smooth_layout.addWidget(self.smooth_apply_btn)
        self.smooth_layout.addStretch()

        # 人脸识别选项卡
        self.face_recognition_tab = QWidget()
        self.face_recognition_layout = QVBoxLayout(self.face_recognition_tab)

        self.face_name_label = QLabel("人脸名称:")
        self.face_name_edit = QLineEdit()
        self.face_register_btn = QPushButton("注册当前人脸")

        self.face_recognition_layout.addWidget(self.face_name_label)
        self.face_recognition_layout.addWidget(self.face_name_edit)
        self.face_recognition_layout.addWidget(self.face_register_btn)

        self.face_list_label = QLabel("已注册人脸:")
        self.face_list = QListWidget()

        self.face_recognition_layout.addWidget(self.face_list_label)
        self.face_recognition_layout.addWidget(self.face_list)
        self.face_recognition_layout.addStretch()

        # 锐化处理选项卡
        self.sharpen_tab = QWidget()
        self.sharpen_layout = QVBoxLayout(self.sharpen_tab)

        self.sharpen_apply_btn = QPushButton("应用锐化")
        self.sharpen_layout.addWidget(self.sharpen_apply_btn)
        self.sharpen_layout.addStretch()

        # 边缘检测选项卡
        self.edge_detection_tab = QWidget()
        self.edge_detection_layout = QVBoxLayout(self.edge_detection_tab)

        self.edge_detection_apply_btn = QPushButton("应用边缘检测")
        self.edge_detection_layout.addWidget(self.edge_detection_apply_btn)
        self.edge_detection_layout.addStretch()

        # 添加选项卡到选项卡控件
        self.settings_tabs.addTab(self.rotate_tab, "旋转")
        self.settings_tabs.addTab(self.scale_tab, "缩放")
        self.settings_tabs.addTab(self.crop_tab, "裁剪")
        self.settings_tabs.addTab(self.color_adjust_tab, "色彩调整")
        self.settings_tabs.addTab(self.smooth_tab, "平滑处理")
        self.settings_tabs.addTab(self.face_recognition_tab, "人脸识别")
        self.settings_tabs.addTab(self.sharpen_tab, "锐化处理")
        self.settings_tabs.addTab(self.edge_detection_tab, "边缘检测")

        # 将选项卡控件添加到设置面板布局
        self.settings_layout.addWidget(self.settings_tabs)

        # 将设置面板添加到主布局
        self.main_layout.addWidget(self.settings_widget, 1)

    def connect_signals(self):
        """连接信号和槽"""
        # 文件操作按钮
        self.load_image_btn.clicked.connect(self.load_image)
        self.load_video_btn.clicked.connect(self.load_video)
        self.open_camera_btn.clicked.connect(self.open_camera)
        self.save_btn.clicked.connect(self.save_result)

        # 图像处理按钮
        self.rotate_btn.clicked.connect(lambda: self.settings_tabs.setCurrentWidget(self.rotate_tab))
        self.scale_btn.clicked.connect(lambda: self.settings_tabs.setCurrentWidget(self.scale_tab))
        self.crop_btn.clicked.connect(self.toggle_crop_mode)
        self.color_adjust_btn.clicked.connect(lambda: self.settings_tabs.setCurrentWidget(self.color_adjust_tab))
        self.smooth_btn.clicked.connect(lambda: self.settings_tabs.setCurrentWidget(self.smooth_tab))
        self.sharpen_btn.clicked.connect(lambda: self.settings_tabs.setCurrentWidget(self.sharpen_tab))
        self.edge_detection_btn.clicked.connect(lambda: self.settings_tabs.setCurrentWidget(self.edge_detection_tab))

        # 计算机视觉按钮
        self.shape_detection_btn.clicked.connect(self.detect_shapes)
        self.face_detection_btn.clicked.connect(self.detect_faces)
        self.face_recognition_btn.clicked.connect(self.recognize_faces)
        self.register_face_btn.clicked.connect(self.register_face)
        self.stitch_images_btn.clicked.connect(self.stitch_images)

    def load_image(self):
        """加载图像"""
        file_path, _ = QFileDialog.getOpenFileName(
            None, "选择图像文件", "", "图像文件 (*.png *.jpg *.jpeg)"
        )
        if file_path:
            if self.processor.load_image(file_path):
                self.display_image(self.processor.current_image)
                self.statusBar.showMessage(f"已加载图像: {file_path}")
            else:
                QMessageBox.warning(None, "错误", "无法加载图像")

    def load_video(self):
        """加载视频"""
        if self.video_thread and self.video_thread.isRunning():
            self.video_thread.stop()

        self.video_thread = VideoThread(self.processor, detection_type='file')
        self.video_thread.change_pixmap_signal.connect(self.update_image)
        self.video_thread.start()

    def open_camera(self):
        """打开摄像头"""
        if self.video_thread and self.video_thread.isRunning():
            self.video_thread.stop()

        self.video_thread = VideoThread(self.processor, detection_type='camera')
        self.video_thread.change_pixmap_signal.connect(self.update_image)
        self.video_thread.start()

    def save_result(self):
        """保存结果"""
        if self.processor.current_image is not None:
            file_path, _ = QFileDialog.getSaveFileName(
                None, "保存图像", "", "图像文件 (*.png *.jpg *.jpeg)"
            )
            if file_path:
                cv2.imwrite(file_path, self.processor.current_image)
                self.statusBar.showMessage(f"已保存图像: {file_path}")
        else:
            QMessageBox.warning(None, "错误", "没有可保存的图像")

    def toggle_crop_mode(self):
        """切换裁剪模式"""
        self.crop_mode = not self.crop_mode
        self.image_canvas.setCropping(self.crop_mode)
        if self.crop_mode:
            self.statusBar.showMessage("裁剪模式已启用")
        else:
            self.statusBar.showMessage("裁剪模式已禁用")

    def on_crop_selected(self, x, y, width, height):
        """处理裁剪信号"""
        cropped_image = self.processor.crop_image(x, y, width, height)
        if cropped_image is not None:
            self.display_image(cropped_image)
            self.statusBar.showMessage("图像已裁剪")

    def detect_shapes(self):
        """形状识别"""
        result, shapes = self.processor.detect_shapes()
        if result is not None:
            self.display_image(result)
            self.results_area.clear()
            for shape in shapes:
                self.results_area.append(f"形状: {shape['type']}, 面积: {shape['area']}")
            self.statusBar.showMessage("形状识别完成")

    def detect_faces(self):
        """人脸检测"""
        result, faces = self.processor.detect_faces()
        if result is not None:
            self.display_image(result)
            self.results_area.clear()
            for face in faces:
                x, y, w, h = face['rect']
                self.results_area.append(f"人脸位置: ({x}, {y}, {w}, {h})")
            self.statusBar.showMessage("人脸检测完成")

    def recognize_faces(self):
        """人脸识别"""
        result, faces = self.processor.recognize_faces()
        if result is not None:
            self.display_image(result)
            self.results_area.clear()
            for face in faces:
                self.results_area.append(f"姓名: {face['name']}, 相似度: {face['similarity']:.1f}%")
            self.statusBar.showMessage("人脸识别完成")

    def register_face(self):
        """注册人脸"""
        name = self.face_name_edit.text().strip()
        if name:
            if self.processor.register_face(name):
                self.face_list.addItem(name)
        else:
            QMessageBox.warning(None, "警告", "请输入人脸名称")

    def stitch_images(self):
        """图像拼接"""
        file_paths, _ = QFileDialog.getOpenFileNames(
            None, "选择要拼接的图像", "", "图像文件 (*.png *.jpg *.jpeg)"
        )
        if len(file_paths) < 2:
            QMessageBox.warning(None, "警告", "至少需要选择两张图像进行拼接")
            return

        images = []
        for file_path in file_paths:
            image = cv2.imread(file_path)
            if image is not None:
                images.append(image)
            else:
                QMessageBox.warning(None, "错误", f"无法加载图像: {file_path}")
                return

        result = self.processor.stitch_images(images)
        if result is not None:
            self.display_image(result)
            self.statusBar.showMessage("图像拼接完成")

    def display_image(self, image):
        """显示图像"""
        height, width, channel = image.shape
        bytes_per_line = 3 * width
        q_img = QImage(image.data, width, height, bytes_per_line, QImage.Format_BGR888)
        pixmap = QPixmap.fromImage(q_img)
        self.image_canvas.setPixmap(pixmap)

    def update_image(self, image):
        """更新图像显示"""
        self.display_image(image)



if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())